import CryptoJS from 'crypto-js'
import { AESEncrypt, AESDecrypt } from './aes'
import { DESEncrypt, DESDecrypt } from './des'
import { SM4Encrypt, SM4Decrypt } from './sm4'
import { generateKey, deriveKey } from './pbkdf2'

// 支持的加密方式（移除GCM，crypto-js不支持真正的GCM）
export enum EncryptMethod {
  AES_256_CBC = 'AES-256-CBC',
  AES_128_CBC = 'AES-128-CBC',
  DES3 = '3DES',
  SM4 = 'SM4'
}

// 默认加密方式
export const DEFAULT_ENCRYPT_METHOD = EncryptMethod.AES_256_CBC

// 加密配置接口
export interface EncryptConfig {
  method: EncryptMethod
  masterPassword: string
  salt?: string
  iterations?: number
}

// 加密结果接口
export interface EncryptResult {
  encrypted: string
  method: string
  version: string
  salt?: string
  iv?: string
}

// 加密工具类
export class CryptoUtil {
  private config: EncryptConfig

  constructor(config: EncryptConfig) {
    this.config = {
      iterations: 10000,
      ...config
    }
  }

  /**
   * 加密数据
   */
  encrypt(plaintext: string): EncryptResult {
    if (!plaintext) {
      throw new Error('待加密数据不能为空')
    }

    const salt = this.config.salt || this.generateSalt()
    const key = this.deriveKey(salt)

    let encrypted: string
    let iv: string | undefined

    switch (this.config.method) {
      case EncryptMethod.AES_256_CBC:
        const aesCbcResult = AESEncrypt(plaintext, key, 'CBC')
        encrypted = aesCbcResult.encrypted
        iv = aesCbcResult.iv
        break

      case EncryptMethod.AES_128_CBC:
        const aes128CbcKey = key.substring(0, 32) // 128位密钥
        const aes128CbcResult = AESEncrypt(plaintext, aes128CbcKey, 'CBC')
        encrypted = aes128CbcResult.encrypted
        iv = aes128CbcResult.iv
        break

      case EncryptMethod.DES3:
        const desResult = DESEncrypt(plaintext, key)
        encrypted = desResult.encrypted
        iv = desResult.iv
        break

      case EncryptMethod.SM4:
        const sm4Result = SM4Encrypt(plaintext, key)
        encrypted = sm4Result.encrypted
        iv = sm4Result.iv
        break

      default:
        throw new Error(`不支持的加密方式: ${this.config.method}`)
    }

    return {
      encrypted,
      method: this.config.method,
      version: '1.0',
      salt: !this.config.salt ? salt : undefined,
      iv
    }
  }

  /**
   * 解密数据
   */
  decrypt(encryptResult: EncryptResult | string): string {
    let result: EncryptResult

    if (typeof encryptResult === 'string') {
      // 兼容旧版本字符串格式
      result = {
        encrypted: encryptResult,
        method: this.config.method,
        version: '1.0'
      }
    } else {
      result = encryptResult
    }

    if (!result.encrypted) {
      throw new Error('加密数据不能为空')
    }

    const salt = result.salt || this.config.salt
    if (!salt) {
      throw new Error('缺少加密盐值')
    }

    const key = this.deriveKey(salt)
    const method = result.method as EncryptMethod

    let decrypted: string

    switch (method) {
      case EncryptMethod.AES_256_CBC:
        decrypted = AESDecrypt(result.encrypted, key, 'CBC', result.iv)
        break

      case EncryptMethod.AES_128_CBC:
        const aes128CbcKey = key.substring(0, 32)
        decrypted = AESDecrypt(result.encrypted, aes128CbcKey, 'CBC', result.iv)
        break

      case EncryptMethod.DES3:
        decrypted = DESDecrypt(result.encrypted, key, result.iv)
        break

      case EncryptMethod.SM4:
        decrypted = SM4Decrypt(result.encrypted, key, result.iv)
        break

      default:
        throw new Error(`不支持的解密方式: ${method}`)
    }

    return decrypted
  }

  /**
   * 生成密钥派生
   */
  private deriveKey(salt: string): string {
    return deriveKey(this.config.masterPassword, salt, this.config.iterations || 10000)
  }

  /**
   * 生成盐值
   */
  private generateSalt(): string {
    return CryptoJS.lib.WordArray.random(16).toString()
  }

  /**
   * 更新主密码
   */
  updateMasterPassword(newPassword: string) {
    this.config.masterPassword = newPassword
  }

  /**
   * 更新加密方式
   */
  updateEncryptMethod(method: EncryptMethod) {
    this.config.method = method
  }
}

// 便捷函数
export const createCrypto = (config: EncryptConfig) => {
  return new CryptoUtil(config)
}

// 密码强度检测
export const checkPasswordStrength = (password: string) => {
  if (!password) {
    return { score: 0, level: 'weak', feedback: '密码不能为空' }
  }

  let score = 0
  const feedback: string[] = []

  // 长度检查
  if (password.length >= 8) {
    score += 1
  } else {
    feedback.push('密码长度至少8位')
  }

  if (password.length >= 12) {
    score += 1
  }

  // 字符类型检查
  if (/[a-z]/.test(password)) {
    score += 1
  } else {
    feedback.push('包含小写字母')
  }

  if (/[A-Z]/.test(password)) {
    score += 1
  } else {
    feedback.push('包含大写字母')
  }

  if (/\d/.test(password)) {
    score += 1
  } else {
    feedback.push('包含数字')
  }

  if (/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?]/.test(password)) {
    score += 1
  } else {
    feedback.push('包含特殊字符')
  }

  // 常见模式检查
  if (!/(.)\1{2,}/.test(password)) {
    score += 1
  } else {
    feedback.push('避免连续相同字符')
  }

  // 等级判断
  let level: 'weak' | 'medium' | 'strong' | 'very-strong'
  if (score <= 2) {
    level = 'weak'
  } else if (score <= 4) {
    level = 'medium'
  } else if (score <= 6) {
    level = 'strong'
  } else {
    level = 'very-strong'
  }

  return {
    score,
    level,
    feedback: feedback.join('、')
  }
}

// 生成随机密码
export const generatePassword = (options: {
  length?: number
  uppercase?: boolean
  lowercase?: boolean
  numbers?: boolean
  symbols?: boolean
  excludeSimilar?: boolean
} = {}) => {
  const {
    length = 16,
    uppercase = true,
    lowercase = true,
    numbers = true,
    symbols = true,
    excludeSimilar = true
  } = options

  let chars = ''

  if (lowercase) {
    chars += excludeSimilar ? 'abcdefghjkmnpqrstuvwxyz' : 'abcdefghijklmnopqrstuvwxyz'
  }

  if (uppercase) {
    chars += excludeSimilar ? 'ABCDEFGHJKMNPQRSTUVWXYZ' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
  }

  if (numbers) {
    chars += excludeSimilar ? '23456789' : '0123456789'
  }

  if (symbols) {
    chars += excludeSimilar ? '!@#$%^&*+-=' : '!@#$%^&*()_+-=[]{}|;:,.<>?'
  }

  if (!chars) {
    throw new Error('至少选择一种字符类型')
  }

  // 使用密码学安全的随机数生成器
  let password = ''
  const randomValues = new Uint32Array(length)
  crypto.getRandomValues(randomValues)

  for (let i = 0; i < length; i++) {
    password += chars.charAt(randomValues[i] % chars.length)
  }

  return password
}

export * from './aes'
export * from './des'
export * from './sm4'
export * from './pbkdf2'